package com.lagou.filter;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.apache.log4j.Logger;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Activate(group = CommonConstants.CONSUMER)
public class TPMonitorFilter implements Filter {

    private Logger logger = Logger.getLogger(this.getClass());

    private Map<String,Cache<String, Long>> methodCallTimeMap = new ConcurrentHashMap<>();

    public TPMonitorFilter(){
        Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(()->{
            try {
                logger.info(" ======= call static ======= ");
                for (Map.Entry<String, Cache<String, Long>> methodCallTimeEntry : methodCallTimeMap.entrySet()) {
                    Cache<String, Long> timeCache = methodCallTimeEntry.getValue();
                    List<Long> times = timeCache.asMap().values().stream().sorted().collect(Collectors.toList());
                    if (!times.isEmpty()) {
                        int tp90Index = times.size() * 90 / 100;
                        int tp99Index = times.size() * 99 / 100;
//                    logger.info(times.size()+" "+tp90Index+" "+tp99Index);
                        logger.info("method: "+methodCallTimeEntry.getKey()+" 过去一分钟调用总次数: "+times.size()+" , TP90: "+ times.get(tp90Index)+" , TP99: "+ times.get(tp99Index));
                    }
                }
            }catch (Exception e){
                logger.error("111",e);
            }

        },0L,5,TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
        if (async) {
            return invoker.invoke(invocation);
        }else {
            long time1 = 0L;
            try {
                time1 = System.currentTimeMillis();
                return invoker.invoke(invocation);
            }finally {
                long duration = System.currentTimeMillis() - time1;
                String methodName = invocation.getMethodName();
                Cache<String, Long> timeCache = methodCallTimeMap.computeIfAbsent(methodName, m -> CacheBuilder.newBuilder()
                        .expireAfterWrite(60, TimeUnit.SECONDS)
                        .build());
                timeCache.put(UUID.randomUUID().toString(),duration);
            }
        }
    }
}
